/**
 * @(#)AgentUtil.java		
 * 
 * History:
 * 1.0	2 May 2012
 * 		Created
 */
package upm.pb4agent.team1.util;

import jade.core.AID;
import jade.core.Agent;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import upm.pb4agent.team1.team.Constant;
import upm.pb4agent.team1.team.Constant.Direction;
import upm.pb4agent.team1.team.Constant.Impulse;

/**
 * @author Bob Nicolaus
 * 
 */
public class AgentUtil {

	private static final DFAgentDescription dfAgentDescription = new DFAgentDescription();
	private static final ServiceDescription serviceDescription = new ServiceDescription();

	private static float impulseWeak;
	private static float impulseMedium;
	private static float impulseStrong;
	private static float frontOarYCoef;
	private static float backOarYCoef;
	private static float frontOarXCoef;
	private static float backOarXCoef;

	private static final String CONFIGURATION_FILE_NAME = "upm/pb4agent/config.properties";
	// private static URL url = null;
	private static Properties properties;
	private static InputStream in = null;

	static {
		// url = AgentUtil.class.getProtectionDomain().getCodeSource()
		// .getLocation();
		in = AgentUtil.class.getClassLoader().getResourceAsStream(
				CONFIGURATION_FILE_NAME);
		properties = new Properties();

		try {
			// properties.load(new FileInputStream(url.getFile()
			// + CONFIGURATION_FILE_NAME));
			properties.load(in);
		} catch (Exception e) {
			System.err.println("Error reading config.properties file: "
					+ e.getMessage());
		}
	}

	/**
	 * Returns the value of a property from a configuration file.
	 * 
	 * @param key
	 * @return the value of a property identified by key
	 * @throws FileNotFoundException
	 * @throws IOException
	 */
	public static String getProperty(String key) {
		return properties.getProperty(key);
	}

	/**
	 * Searches an agent with a specific DF type.
	 * 
	 * @param requestingAgent
	 *            Agent that request the searching.
	 * @param type
	 *            The target agent DF type
	 * @return The target agent AID
	 * @throws FIPAException
	 */
	public static AID searchAgentInDF(Agent requestingAgent, String type)
			throws FIPAException, InterruptedException {
		AID agentAID = null;
		DFAgentDescription[] result = null;

		serviceDescription.setType(type);
		dfAgentDescription.addServices(serviceDescription);

		while (result == null) {
			result = DFService.search(requestingAgent, dfAgentDescription);
			if (result.length > 0) {
				agentAID = (AID) result[0].getName();
			} else {
				Thread.sleep(1000);
			}
		}

		return agentAID;
	}

	private static float getImpulseWeak() {
		if (impulseWeak == 0) {
			impulseWeak = Float.valueOf(getProperty("impulse_weak"));
		}

		return impulseWeak;
	}

	private static float getImpulseMedium() {
		if (impulseMedium == 0) {
			impulseMedium = Float.valueOf(getProperty("impulse_medium"));
		}

		return impulseMedium;
	}

	private static float getImpulseStrong() {
		if (impulseStrong == 0) {
			impulseStrong = Float.valueOf(getProperty("impulse_strong"));
		}

		return impulseStrong;
	}

	private static float getFrontOarYCoef() {
		if (frontOarYCoef == 0) {
			frontOarYCoef = Float.valueOf(getProperty("front_oar_y_coef"));
		}

		return frontOarYCoef;
	}

	private static float getBackOarYCoef() {
		if (backOarYCoef == 0) {
			backOarYCoef = Float.valueOf(getProperty("back_oar_y_coef"));
		}

		return backOarYCoef;
	}

	private static float getFrontOarXCoef() {
		if (frontOarXCoef == 0) {
			frontOarXCoef = Float.valueOf(getProperty("front_oar_x_coef"));
		}

		return frontOarXCoef;
	}

	private static float getBackOarXCoef() {
		if (backOarXCoef == 0) {
			backOarXCoef = Float.valueOf(getProperty("back_oar_x_coef"));
		}

		return backOarXCoef;
	}

	/**
	 * Gets the real value of impulse. The values are read from the
	 * config.properties.
	 * 
	 * @param impulse
	 *            Enum type {Weak, Medium, Strong}
	 * @return
	 */
	public static float getImpulseValue(int impulse) {
		float value = 0;

		if (impulse == Impulse.Weak.ordinal()) {
			value = getImpulseWeak();
		} else if (impulse == Impulse.Medium.ordinal()) {
			value = getImpulseMedium();
		} else if (impulse == Impulse.Strong.ordinal()) {
			value = getImpulseStrong();
		}

		return value;
	}

	/**
	 * Calculates the rower tiredness.
	 * 
	 * @param previousTiredness
	 *            The rower tiredness
	 * @param impulse
	 *            The rower intention for current request
	 * @param resistance
	 *            The rower resistance
	 * @return the rower current tiredness
	 */
	public static float calculateTotalTiredness(float previousTiredness,
			int impulse, float resistance) {
		float result = 0;

		result = AgentUtil.getImpulseValue(impulse) * (1 - resistance);

		if (result > 1) {
			result = 1;
		}

		return result;
	}

	/**
	 * Calculates the boat movement on X axis based on rower impulse, strength,
	 * tiredness, and oar ID.
	 * 
	 * @param impulse
	 *            The rower intention for current request
	 * @param strength
	 *            The rower strength
	 * @param tiredness
	 *            The rower current tiredness
	 * @param oarID
	 *            The rower oar ID
	 * @return
	 */
	public static int calculateMovementX(int impulse, float strength,
			float tiredness, String oarID) {
		int movement = 0;
		float oarCoef = 0;

		if (oarID.equalsIgnoreCase(Constant.oar1)
				|| oarID.equalsIgnoreCase(Constant.oar2)) {
			oarCoef = getFrontOarXCoef();
		} else if (oarID.equalsIgnoreCase(Constant.oar3)
				|| oarID.equalsIgnoreCase(Constant.oar4)) {
			oarCoef = getBackOarXCoef();
		}

		movement = (int) Math.ceil(AgentUtil.getImpulseValue(impulse)
				* (strength - tiredness) * oarCoef);

		if (oarID.equalsIgnoreCase(Constant.oar2)
				|| oarID.equalsIgnoreCase(Constant.oar4)) {
			movement = movement * -1;
		}

		return movement;
	}

	/**
	 * Calculates the boat movement on Y axis based on rower impulse, strength,
	 * tiredness, oar ID, and row direction
	 * 
	 * @param impulse
	 *            The rower intention for current request
	 * @param strength
	 *            The rower strength
	 * @param tiredness
	 *            The rower current tiredness
	 * @param oarID
	 *            The rower oar ID
	 * @param direction
	 *            The rower direction of rowing
	 * @return
	 */
	public static int calculateMovementY(int impulse, float strength,
			float tiredness, String oarID, int direction) {
		int movement = 0;
		float oarCoef = 0;

		if (oarID.equalsIgnoreCase(Constant.oar1)
				|| oarID.equalsIgnoreCase(Constant.oar2)) {
			oarCoef = getFrontOarYCoef();
		} else if (oarID.equalsIgnoreCase(Constant.oar3)
				|| oarID.equalsIgnoreCase(Constant.oar4)) {
			oarCoef = getBackOarYCoef();
		}

		movement = (int) Math.ceil(AgentUtil.getImpulseValue(impulse)
				* (strength - tiredness) * oarCoef);

		if (movement <= 0) {
			System.out.println(">>>>>>>>>>>>>>>>>>>> strength: " + strength);
			System.out.println(">>>>>>>>>>>>>>>>>>>> tiredness: " + tiredness);
		}

		if (direction == Direction.Backward.ordinal()) {
			movement = movement * -1;
		}

		return movement;
	}
}
